﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using EnvDTE;
using EnvDTE80;
using System.Reflection;
using OECModeler.Model;
using System.Xml.Serialization;
using OCESandbox;

namespace OECModeler.UI
{
    public partial class Main : Form
    {
        private List<Project> _projects=new List<Project>();
        private DTE2 _application;
        private Project _current;
        private Classes transformer;
        private string settingsFile ;
        private string _ocePath;
        private string _projectPath;
        private AppDomain _localAppDomain;
        private ProxyDomain _proxyDomain;

        public Main(DTE2 applicationObject)
        {
            InitializeComponent();
            this.dgv.AutoGenerateColumns = false;
            _application = applicationObject;
            foreach (Project i in _application.Solution.Projects)
            {
                LoadProjects(i);
            }
            cbProjects.DataSource = _projects;
            cbProjects.SelectedIndex = 0;
            cbProjects.DisplayMember = "Name";
            
        }
        private void LoadProjects(Project project)
        {
            //SL Class library
            if (project.Kind != ProjectKinds.vsProjectKindSolutionFolder)
                _projects.Add(project);
            if (project.Kind == ProjectKinds.vsProjectKindSolutionFolder)
            {
                List<Project> list = new List<Project>();
                for (var i = 1; i <= project.ProjectItems.Count; i++)
                {
                    var subProject = project.ProjectItems.Item(i).SubProject;
                    if (subProject == null)
                    {
                        continue;
                    }
                    else
                    {
                        list.Add(subProject);
                    }
                }
                list.ForEach(LoadProjects);
            }
        }

        private bool ReadMappings()
        {
            try
            {
                if (File.Exists(settingsFile))
                {
                    Clear();
                    XmlSerializer serializer = new XmlSerializer(typeof(List<ClassInformation>));
                    TextReader tr = new StreamReader(settingsFile);
                    var obj = (List<ClassInformation>)serializer.Deserialize(tr);
                  
                    foreach (var i in obj)
                    {
                        if (i != null)
                            transformer.Entities.Add(i); 
                    }
                    tr.Close();
                    bindingSource1.DataSource = transformer.Entities;
                    return true;
                }
                else
                {
                    MessageBox.Show("Mapping file is not found.");
                    return false;
                }
            }
            catch (Exception)
            {
                MessageBox.Show("Error while reading the mapping file.");
                return false;
            }

        }

         private void SaveMappings(ProjectItem dir)
         {
             XmlSerializer serializer = new XmlSerializer(typeof(List<ClassInformation>));
             TextWriter tw = new StreamWriter(settingsFile);
             serializer.Serialize(tw, transformer.Entities);
             tw.Close();
             dir.ProjectItems.AddFromFile(settingsFile);
         }

         private void Clear()
         {
             transformer = new Classes();
             transformer.Entities = new List<ClassInformation>();
             bindingSource1.Clear();
             dgv.Rows.Clear();
         }

         private void CreateNewApplicationDomain()
         {
             AppDomainSetup setup = new AppDomainSetup();
             setup.ApplicationBase = Path.GetDirectoryName(SandboxInformation.CurrentAssemblyLocation);
             //setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
             //setup.ApplicationBase = _projectPath;
             _localAppDomain = AppDomain.CreateDomain("OCEAPPDOMAIN", null, setup);
             AppDomain.CurrentDomain.AssemblyResolve+=CurrentDomain_AssemblyResolve;

             _proxyDomain = (OCESandbox.ProxyDomain)_localAppDomain.CreateInstanceAndUnwrap("OCESandbox", "OCESandbox.ProxyDomain");
             _localAppDomain.SetupInformation.ApplicationBase = _projectPath;
         }

         private void UnloadTheAppdomain()
         {
             if(_localAppDomain!=null)
                 AppDomain.Unload(_localAppDomain);
         }

         private void btbDetect_Click(object sender, EventArgs e)
         {
             if (ReadMappings())
             {
                 return;
             }
             if (cbProjects.SelectedIndex == -1) return;
             var project = _projects[cbProjects.SelectedIndex];
             _current = project;
             UnloadTheAppdomain();
             CreateNewApplicationDomain();
             string filename = project.Properties.Item("OutputFileName").Value.ToString();
             //try to load assembly automatically
             string outputfile = string.Format("{0}\\{1}", _projectPath, filename);
             transformer = new Classes();
             transformer.Entities = new List<ClassInformation>();

             AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
             Assembly assembly = null;
             ClassInformation[] entities = null;
             if (File.Exists(outputfile))
             {
                 //assembly = Assembly.LoadFrom(outputfile);
                 entities = _proxyDomain.LoadAssemblyInMyDomain(outputfile);
             }
             else //try debug
             {
                 outputfile = string.Format("{0}\\bin\\debug\\{1}", _projectPath, filename);
                 if (File.Exists(outputfile))
                 {
                     //assembly = Assembly.LoadFrom(outputfile);
                     entities = _proxyDomain.LoadAssemblyInMyDomain(outputfile);
                 }
                 else
                 {
                     outputfile = string.Format("{0}\\bin\\{1}", _projectPath, filename);
                     if (File.Exists(outputfile))
                     {
                         //assembly = Assembly.LoadFrom(outputfile);
                         entities = _proxyDomain.LoadAssemblyInMyDomain(outputfile);
                     }
                     else
                     {
                         MessageBox.Show(outputfile);
                         openFileDialog1.InitialDirectory = Path.GetDirectoryName(project.FullName);
                         openFileDialog1.Filter = string.Format("Library (*.dll)|*.dll|Executable file(*.exe)|*.exe");
                         openFileDialog1.FileName = filename;
                         openFileDialog1.Title = string.Format("Looking for ({0})", outputfile);
                         if (openFileDialog1.ShowDialog() == DialogResult.OK)
                         {
                             //assembly = Assembly.LoadFrom(openFileDialog1.FileName);
                             entities = _proxyDomain.LoadAssemblyInMyDomain(openFileDialog1.FileName);
                         }
                     }
                 }
             }
             if (entities == null) return;
             Clear();
             //Type[] entities = assembly.GetTypes();
             foreach (var entity in entities)
             {
                 transformer.Entities.Add(entity);
                 bindingSource1.Add(entity);
             }
         }
        

        Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            if (_current != null)
            {
                string filename = _current.Properties.Item("OutputFileName").Value.ToString();
                if (filename.Contains(args.Name.Split(',')[0]))
                {
                    //try to load assembly automatically
                    string outputfile = string.Format("{0}\\{1}", _projectPath, filename);
                    Assembly assembly = Assembly.LoadFrom(outputfile);
                    return assembly;
                }
            }
            if(args.Name=="OCESandbox, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d8576b3350128d9d")
            {
                Assembly assembly= Assembly.LoadFrom(SandboxInformation.CurrentAssemblyLocation);
                return assembly;
            }

            string slpath =Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
            string assemblyfileName = new AssemblyName(args.Name).Name + ".dll";
            string path = string.Format(@"{0}\Reference Assemblies\Microsoft\Framework\Silverlight\v5.0\{1}", slpath, assemblyfileName);
            if(File.Exists(path))
            {
                return Assembly.LoadFrom(path);
            }
            throw new Exception(string.Format("Sorry, this assembly {0} is not loaded.",args.Name));
        }

        private void dgv_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {

        }

        private void Main_Load(object sender, EventArgs e)
        {
            this.dgv.DataSource = bindingSource1;
        }

        private void btnGenerateClasses_Click(object sender, EventArgs e)
        {
           GenerateMapping();
        }

         private void GenerateMapping()
         {
             var text = transformer.TransformText();
             try
             {
                 string filePath = Path.Combine(_projectPath, "OCE_GenerateCode\\oce_boilerplate.cs");
                 string ocefolderpath = Path.Combine(_projectPath, "OCE_GenerateCode");
                 settingsFile = Path.Combine(_ocePath, string.Format("{0}.ocemap", _current.Name));

                 ProjectItem dir;
                 if (!Directory.Exists(ocefolderpath))
                     dir = _current.ProjectItems.AddFolder("OCE_GenerateCode");
                 else
                 {
                     dir = _current.ProjectItems.AddFromDirectory(ocefolderpath);
                 }

                 //create the file
                 File.WriteAllText(filePath, text);
                 dir.ProjectItems.AddFromFile(filePath);
                 _application.ItemOperations.OpenFile(filePath);

                 SaveMappings(dir);
             }
             catch (Exception ex)
             {
                 MessageBox.Show(ex.Message);
             }
             this.Close();
         }

         private void Main_FormClosing(object sender, FormClosingEventArgs e)
        {
            UnloadTheAppdomain();
        }

        private void cbProjects_SelectedIndexChanged(object sender, EventArgs e)
        {

            if (cbProjects.SelectedIndex == -1) return;
            var project = _projects[cbProjects.SelectedIndex];
            _current = project;
           
            _projectPath = Path.GetDirectoryName(_current.FullName).ToString();
            _ocePath = Path.Combine(_projectPath, "OCE_GenerateCode");
            settingsFile = Path.Combine(_ocePath, string.Format("{0}.ocemap", _current.Name));
        }

        private void tableLayoutPanel1_Paint(object sender, PaintEventArgs e)
        {
            
        }

        private void btnLoad_Click(object sender, EventArgs e)
        {
            ReadMappings();
        }
    }
}
